import 'package:device_calendar/device_calendar.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:photo_frame/net/http_manager.dart';
import 'package:photo_frame/net/model/e_tag.dart';
import 'package:photo_frame/net/model/event_dto.dart';
import 'package:photo_frame/net/model/event_state.dart';
import 'package:photo_frame/utils/a_logger.dart';
import 'package:photo_frame/utils/calendar_helper.dart';
import 'package:provider/provider.dart';
import 'package:sqflite/sqflite.dart';

ScheduleViewModel _scheduleViewModel = ScheduleViewModel();

class ScheduleProvider extends ChangeNotifierProvider<ScheduleViewModel> {
  ScheduleProvider.value() : super.value(value: _scheduleViewModel);
}

class ScheduleViewModel extends ChangeNotifier {
  static late EventsDBHelper _dbHelper;
  static late EventStateDao _eventStateDaoDBHelper;
  Map<String, List<EventDTO>> _allEvent = {};

  Map<String, List<EventDTO>> get allEvent => _allEvent;
  String? lastFrameId = null;

  static init() async {
    _dbHelper = EventsDBHelper();
    await _dbHelper.init();
    _eventStateDaoDBHelper = EventStateDao();
    await _eventStateDaoDBHelper.init();
    if (kDebugMode) {
      // _dbHelper.deleteAll();
    }
  }

  fetchEvents({
    num? from,
    num? to,
    String? frameId,
  }) {
    lastFrameId = frameId;
    Future<List<EventDTO>?> future;
    var test = true;
    test = false;
    // if (from == null || test) {
    future = _apiEvents(frameId ?? "");
    // } else {
    //   future = _apiTimestampEvents(frameId ?? "", from, to);
    // }
    return Future.wait([_apiEventsStates(frameId ?? ""), future])
        .then((futureRes) async {
      var apiEventState = futureRes[0] != null
          ? futureRes[0] as List<ScheduleEventStateEntity>
          : null;
      var e = futureRes[1] != null ? futureRes[1] as List<EventDTO> : null;
      if (e != null) {
        if (test) {
          delete(e);
          _dbHelper.deleteAll();
        } else {
          Map<int, EventDTO> remoteEvents = {for (var i in e) i.id!: i};
          // List<EventDTO> localEvents = (await _dbHelper.queryAll())
          //     .where((entry) =>
          //         entry.frame_id == frameId &&
          //         (entry.dtstart ?? 0) >= (from ?? 0))
          //     .toList();
          List<EventDTO> localEvents = (await _dbHelper.queryAll())
              .where((entry) => entry.frame_id == frameId)
              .toList();
          var filterEvent = {
            for (var i in localEvents.where((entry) => entry.id != null))
              i.id!: i
          };
          ALogger.e(
              "_fetchEvents filterEvent-local(${filterEvent.length}): ${localEvents.map((e) {
            return {
              "id": e.id,
              "title": e.title,
            };
          })} ");
          ALogger.e("_fetchEvents filterEvent-remote: ${e.map((e) {
            return {
              "title": e.title,
              "event-id": e.event_id,
            };
          })} ");

          await _syncRemote(remoteEvents, filterEvent);
          await _uploadLocalEvent(
              localEvents.where((e) => e.id == null && e.action == 0).toList());
          // 获取所有事件状态
          final allState = await _eventStateDaoDBHelper.getAll();
          print(
              "XDBRepository.getScheduleEventStateDao()-all: ${allState.length}  ${apiEventState?.length ?? 0}");

          // 获取所有事件
          List<EventDTO> finalEvents = localEvents =
              (await _dbHelper.queryAll())
                  .where((entry) => entry.frame_id == frameId)
                  .toList();

          // 1. 删除本地所有不存在事件的时间戳
          await _eventStateDaoDBHelper.deleteByExcludedIds(
              finalEvents.map((event) => event.id ?? 0).toList(),
              frameId ?? "");

          // 2. 删除事件开始时间前的时间戳
          for (var event in finalEvents) {
            await _eventStateDaoDBHelper.deleteByIdAndTime(
                event.id ?? 0,
                normalizeToMidnight(DateTime.fromMillisecondsSinceEpoch(
                        event.dtstart ?? 0,
                        isUtc: true))
                    .millisecondsSinceEpoch);
          }

          // 3. 远端时间戳直接插入本地，数据库自动去重
          if (apiEventState != null) {
            for (var state in apiEventState) {
              //绑定state eventAutoId
              var autoId2 = finalEvents
                  .where((e) => e.id == state.id)
                  .firstOrNull
                  ?.autoId;

              await _eventStateDaoDBHelper
                  .insert(state.copyWith(eventAutoId: autoId2));
            }
          }
          print(
              "XDBRepository.getScheduleEventStateDao()-all: ${(await _eventStateDaoDBHelper.getAll()).length}  ${apiEventState?.length ?? 0}");

          final apiEventStateMap =
              apiEventState?.fold<Map<String, ScheduleEventStateEntity>>(
            {},
            (map, state) {
              map[state.key()] = state;
              return map;
            },
          );

          // 本地 vs 远端，上传需要上传的时间戳
          final needUpload =
              (await _eventStateDaoDBHelper.getAll()).where((localState) {
            return apiEventStateMap?[localState.key()] == null;
          }).map((localState) {
            return [frameId, localState.id, localState.time];
          }).toList();

          // 上传需要同步的数据
          if (needUpload.isNotEmpty) {
            addStatus(needUpload);
          }
        }
      }
      return e;
    }).catchError((err, stack) {
      ALogger.e("_fetchEvents catchError: ${err} ");
      ALogger.e("_fetchEvents catchError: ${stack} ");
      return List.empty();
    }).whenComplete(() {
      _onDataSetChanged();
    });
  }

  _apiEvents(String frameId) {
    return HttpManager.instance.doRequest("/api/getevents",
        parameters: {'frame_id': frameId}).then<List<EventDTO>?>((e) async {
      if (e.isSuccess && e.data['events'] != null) {
        var events = e.data['events'] as List<dynamic>;
        var list = events
            .map((e) => EventDTO.fromJson(e))
            .where((e) => e.frame_id != null && e.id != null)
            .toList();
        ALogger.e(
            "_fetchEvents api/getevents : ${list.map((e) => "==${e.attendees}==")} ");
        if (kDebugMode) {}
        return list;
      }
      return null;
    });
  }

  Future<List<ScheduleEventStateEntity>?> _apiEventsStates(String frameId) {
    return HttpManager.instance.doRequest("/api/get_all_status", parameters: {
      'ids': [frameId]
    }).then<List<ScheduleEventStateEntity>?>((e) async {
      if (e.isSuccess && e.data is List<dynamic>) {
        var status = e.data as List<dynamic>;
        var list = status
            .map((e) => ScheduleEventStateEntity(
                frame_id: e['frame_id'], id: e['event_id'], time: e['time']))
            .toList();
        ALogger.e("last timestamp ${list.last.time}");
        return list;
      }
      return null;
    }).catchError((e) {
      return null;
    });
  }

  _apiTimestampEvents(String frameId, num from, num? to) {
    return HttpManager.instance.doRequest("/api/geteventscope", parameters: {
      'frame_id': frameId,
      'from': from,
      'to': to
    }).then<List<EventDTO>?>((e) async {
      if (e.isSuccess && e.data['events'] != null) {
        var events = e.data['events'] as List<dynamic>;
        var list = events
            .map((e) => EventDTO.fromJson(e))
            .where((e) => e.frame_id != null && e.id != null)
            .toList();
        ALogger.e("_fetchEvents api/geteventscope : ${list.length} ");
        return list;
      }
      return null;
    });
  }

  Future<void> _syncRemote(
      Map<num, EventDTO> remoteEvents, Map<num, EventDTO> localEvents) async {
    ALogger.e("_syncRemote enter ${localEvents.length} ");

    List<EventDTO> uploadLocalEvents = [];
    List<EventDTO> deleteRemoteEvents = [];
    remoteEvents.forEach((id, remoteEvent) async {
      var localEvent = localEvents[id];
      if (localEvent == null) {
        //insert local
        ALogger.e("_syncRemote createOrUpdateEvent: ");
        remoteEvent.event_id = null;
        var _event_id =
            DateTime.now().toUtc().millisecondsSinceEpoch.toString();
        remoteEvent.event_id = _event_id;
        try {
          await _dbHelper.insert(remoteEvent);
        } catch (e) {
          ALogger.e(e);
        }
      } else if ((remoteEvent.etag ?? 0) > (localEvent.etag ?? 0)) {
        //update local
        await _dbHelper.update(remoteEvent.copyWithMap(
            autoId: localEvent.autoId, event_id: localEvent.event_id));
      } else if ((remoteEvent.etag ?? 0) < (localEvent.etag ?? 0)) {
        //update remote
        if (localEvent.action == 1) {
          uploadLocalEvents
              .add(localEvent.copyWithMap(event_id: remoteEvent.event_id));
        } else if (localEvent.action == 2) {
          // delete remote
          deleteRemoteEvents.add(localEvent);
        }
      }
    });
    ALogger.e("local existxxxx2222 ");
    await _updateRemoteEvent(uploadLocalEvents);
    await delete(deleteRemoteEvents);
    ALogger.e("local existxxxx ");
    await Future.wait(localEvents.values
        .map((localEvent) {
          var exist = remoteEvents[localEvent.id] != null;
          // ALogger.e("remoteEvents exist ${localEvent.id} ");
          if (!exist) {
            return _dbHelper.delete(localEvent.id!);
          } else {
            return null;
          }
        })
        .where((e) => e != null)
        .map((e) => e!));
  }

  Future<void> _updateRemoteEvent(List<EventDTO> events) async {
    if (events.isEmpty) {
      return;
    }
    ALogger.e(
        "_updateRemoteEvent-enter ${events[0].dtstart}  ${events[0].autoId}");
    await Future.wait(
        events.map((e) => _dbHelper.update(e.copyWithMap(action: 1, sync: 0))));
    var filterEvents = events.where((e) => e.id != null);
    if (filterEvents.isEmpty) {
      return;
    }
    return HttpManager.instance.doRequest("/api/updateevents", parameters: {
      'events': filterEvents.map((e) {
        var map = e.toMap()
          ..['attendees'] = e.memberList
          ..remove('sync')
          ..remove('action');
        return map;
      }).toList(),
    }).then((e) async {
      if (e.isSuccess) {
        await Future.wait(filterEvents
            .map((e) => _dbHelper.update(e.copyWithMap(action: -1, sync: 1))));
      }
    }).catchError((e) {
      ALogger.e("_updateRemoteEvent ${e}");
    });
  }

  _uploadLocalEvent(List<EventDTO> events) async {
    for (var e in events) {
      ALogger.e("_upload enter: ${{"title": e.title, "eventId": e.event_id}}");
    }

    if (events.isEmpty) {
      return null;
    }
    await Future.wait(
        events.map((e) => _dbHelper.update(e.copyWithMap(action: 0, sync: 0))));
    return HttpManager.instance.doRequest("/api/addevents", parameters: {
      'events': events
          .map((e) => e.toMap()
            ..['attendees'] = e.memberList
            ..remove('sync')
            ..remove('action'))
          .toList(),
    }).then((e) async {
      if (e.isSuccess && e.data != null && e.data['ids'] != null) {
        var etags = (e.data['ids'] as List<dynamic>)
            .map((e) => ETag.fromJson(e))
            .toList();
        var eTagMap = {for (var item in etags) item.event_id: item};
        await Future.wait(events.map((e) {
          var remoteEvent = eTagMap.remove(e.event_id);
          if (remoteEvent?.id != null) {
            var copyWithMap =
                e.copyWithMap(id: remoteEvent!.id, action: -1, sync: 1);
            ALogger.e("_upload updatelocal : ${e.autoId} $copyWithMap");

            return _dbHelper.update(copyWithMap);
          }
          return Future.value("");
        }));
      }
    }).catchError((e) {
      ALogger.e("uploadLocalEvent errror ${e}");
    });
  }

  Future<dynamic> addMultiple(List<EventDTO> events,
      {bool isSystem = false}) async {
    Set<String> seenEventIds = {};
    List<EventDTO> uniqueEvents = [];

    for (var event in events) {
      if (!seenEventIds.contains(event.event_id)) {
        seenEventIds.add(event.event_id!);
        uniqueEvents.add(event);
      }
    }
    events = uniqueEvents;
    ALogger.w("event-add : ${events.map((e) => {
          "title": e.title,
          "dstart": e.dtstart
        })}  isSystem : ${isSystem}  ");
    for (var event in events) {
      event.action = 0;
      event.refreshETag();
      await _dbHelper.insert(event).then((e) {
        ALogger.e("insert success: ${event.title} ${e}");
        event.autoId = e;
      }).catchError((_) {
        ALogger.e("insert error: $_");
      });
    }
    await _uploadLocalEvent(events);
    _onDataSetChanged();
  }

  Future<dynamic> add(EventDTO event, {bool isSystem = false}) async {
    ALogger.w("event-add : ${event}  isSystem : ${isSystem}  ${event.dtstart}");
    event.action = 0;
    event.event_id = DateTime.now().toUtc().millisecondsSinceEpoch.toString();
    event.refreshETag();
    await _dbHelper.insert(event).then((e) {
      event.autoId = e;
    }).catchError((_) {
      ALogger.e("insert error: $_");
    });
    await _uploadLocalEvent([event]);
    _onDataSetChanged();
  }

  Future<void> update(EventDTO event) async {
    ALogger.e("update-enter ${event}");
    event.action = 1;
    event.refreshETag();
    await _dbHelper.update(event).catchError((e) {
      print(e);
    });
    // var queryByIdAndTime = await _eventStateDaoDBHelper.queryByIdAndTime(
    //     event.id ?? 0,
    //     normalizeToMidnight(DateTime.fromMillisecondsSinceEpoch(
    //         event.dtstart ?? 0,
    //         isUtc: true))
    //         .millisecondsSinceEpoch);
    // List<List<Object>> status = queryByIdAndTime.map((e) =>
    // [
    //   event.frame_id ?? "",
    //   event.id ?? 0,
    //   e.time ?? 0
    // ]).toList();
    // //删除开始事件前时间戳
    // if (status.isNotEmpty) {
    //   delStatus(status);
    // }
    // await _eventStateDaoDBHelper.deleteByIdAndTime(
    //     event.id ?? 0,
    //     normalizeToMidnight(DateTime.fromMillisecondsSinceEpoch(
    //         event.dtstart ?? 0,
    //         isUtc: true))
    //         .millisecondsSinceEpoch);
    if (event.isFotocube() == true) {
      // await CalendarHelper.instance
      //     .createOrUpdateEvent(event)
      //     .catchError((e) {});
    }
    await _updateRemoteEvent([event]);
    _onDataSetChanged();
  }

  /**
   * curDay的dstart对应的是每天
   */
  Future<void> finishEventWithTimestamp(
      EventDTO curEvent, int timestamp) async {
    var event = await _dbHelper.getById(curEvent.id ?? 0);
    if (event == null) {
      return;
    }
    print("finishEventWithTimestamp: $event");

    final int? id = event.id;

    try {
      // 1. 将事件设置为更新
      event.action = 1;
      event.refreshETag();
      await _dbHelper.update(event).catchError((e) {
        print(e);
      });

      final entity = ScheduleEventStateEntity(
        id: id,
        frame_id: event.frame_id,
        eventAutoId: event.autoId,
        time: normalizeToMidnight(
                DateTime.fromMillisecondsSinceEpoch(timestamp, isUtc: true))
            .millisecondsSinceEpoch,
      );

      // 2. 插入本地时间戳
      _eventStateDaoDBHelper.insert(entity);

      // 3. 更新远端时间戳
      await addStatus([
        [event.frame_id, id, timestamp]
      ]).then((response) async {
        if (response.isSuccess == true) {
          // 4.更新远端事件
          await _updateRemoteEvent([event!]);
        }
      }).whenComplete(() {
        _onDataSetChanged();
      });
    } catch (e) {
      print("Error: $e");
    }
  }

  Future<BaseResponse> addStatus(List<List<dynamic>> status) {
    return HttpManager.instance.doRequest("/api/add_status", parameters: {
      'status': status,
    });
  }

  Future<BaseResponse> delStatus(List<List<Object>> status) {
    return HttpManager.instance.doRequest("/api/del_status", parameters: {
      'status': status,
    });
  }

  Future<List<EventDTO>?> queryEvent(
      String? frameId, DateTime startDateTime, DateTime endDateTime) async {
    if (frameId == null) {
      return null;
    }
    startDateTime = normalizeToMidnight(startDateTime);
    endDateTime = normalizeToEndOfDay(endDateTime);

    var events = _allEvent[frameId];
    // ALogger.i(
    //     "queryEvent frameId=$frameId events=${events?.length} startDateTime=$startDateTime endDateTime=$endDateTime");
    if (events == null) {
      return null;
    }
    List<EventDTO> filteredEvents = [];
    for (var event in events) {
      var dtstart = event.dtstart ?? 0;
      var eventStartDayTimestamp = normalizeToMidnight(
              DateTime.fromMillisecondsSinceEpoch(dtstart, isUtc: true))
          .millisecondsSinceEpoch;
      // ALogger.i(
      //     "queryEvent frameId=$frameId startDateTime=${startDateTime.millisecondsSinceEpoch}  eventStartDateTime=${eventStartDayTimestamp} ");
      var eventStartDateTime =
          DateTime.fromMillisecondsSinceEpoch(dtstart, isUtc: true);
      if (startDateTime.millisecondsSinceEpoch >= eventStartDayTimestamp) {
        var frequency = event.toRecurrenceRule()?.recurrenceFrequency;
        var localEventStartDateTime = eventStartDateTime.toLocal();
        var localStartDateTime = startDateTime.toLocal();
        Future<EventDTO> transformEvent() async {
          var dateTime = DateTime(
            localStartDateTime.year,
            localStartDateTime.month,
            localStartDateTime.day,
            localEventStartDateTime.hour,
            localEventStartDateTime.minute,
            localEventStartDateTime.second,
          );
          var todayFinish = (await _eventStateDaoDBHelper
                          .selectByEventAutoId(event.autoId))
                      ?.where((e) {
                    //是否标记为已完成
                    return normalizeToMidnight(
                                DateTime.fromMillisecondsSinceEpoch(e.time ?? 0,
                                    isUtc: true))
                            .millisecondsSinceEpoch ==
                        startDateTime.millisecondsSinceEpoch;
                  }).isNotEmpty ==
                  true
              ? 1
              : 0;

          // var where = (await _eventStateDaoDBHelper.getAll());
          // if (where != null) {
          //   for (var l in where) {
          //     ALogger.e(
          //         "todayFinish ${event.title} ${l.time} ${l.eventAutoId}");
          //   }
          // }

          var dtstartTimestamp = dateTime.millisecondsSinceEpoch;
          // ALogger.i(
          //     "foreach  startDateTime=${startDateTime.millisecondsSinceEpoch} eventStartTime=${eventStartDayTimestamp}:${eventStartDateTime}:${startDateTime}:${dtstartTimestamp} ${event.title} ${frequency}");
          return event.copyWithMap(
              dtstart: dtstartTimestamp,
              dtend: dtstartTimestamp + ((event.dtend ?? dtstart) - dtstart),
              todayFinish: todayFinish);
        }

        switch (frequency) {
          case RecurrenceFrequency.Daily:
            filteredEvents.add(await transformEvent());
            break;
          case RecurrenceFrequency.Weekly:
            ALogger.i(
                "RecurrenceFrequency.Weekly  eventStartDateTime=${eventStartDateTime.weekday}:${eventStartDateTime.millisecondsSinceEpoch}} startDateTime=${startDateTime.weekday}:${startDateTime.millisecondsSinceEpoch}");
            if (localEventStartDateTime.weekday == localStartDateTime.weekday) {
              filteredEvents.add(await transformEvent());
            }
            break;
          case RecurrenceFrequency.Monthly:
            if (localEventStartDateTime.day == localStartDateTime.day ||
                (isLastDayOfMonth(localEventStartDateTime) &&
                    isLastDayOfMonth(localEventStartDateTime))) {
              filteredEvents.add(await transformEvent());
            }
            break;
          case RecurrenceFrequency.Yearly:
            if (areSameDayInDifferentYears(
                    localEventStartDateTime, localStartDateTime) ||
                (isLastDayOfYear(localEventStartDateTime) &&
                    isLastDayOfYear(localStartDateTime))) {
              filteredEvents.add(await transformEvent());
            }
            break;
          case null:
            if (startDateTime.millisecondsSinceEpoch ==
                eventStartDayTimestamp) {
              filteredEvents.add(await transformEvent());
            }
        }
      }
    }
    ALogger.i(
        "queryEvent frameId=$frameId res=${filteredEvents.length.toString() ?? 0} "
        "enter-start:${startDateTime.millisecondsSinceEpoch} "
        "enter-end: ${endDateTime.millisecondsSinceEpoch}");
    return filteredEvents;
  }

  Future<void> delete(List<EventDTO> events) {
    if (events.isEmpty) {
      return Future.value();
    }
    for (var event in events) {
      event.action == 2;
      event.refreshETag();
    }
    return HttpManager.instance.doRequest("/api/delevents", parameters: {
      'ids': events.map((e) => e.id).toList(),
    }).then((e) {
      if (e.isSuccess) {
        for (var event in events) {
          _allEvent[event.frame_id]?.removeWhere((e) => e.id == event.id);
        }
        _deleteLocalEvent(events);
      }
    }).whenComplete(() {
      _onDataSetChanged();
    });
  }

  Future<void> _deleteLocalEvent(List<EventDTO> events) async {
    ALogger.e("_deleteLocalEvent ${events.length}");
    await Future.wait(events.map((e) => _dbHelper.delete(e.id ?? -1)));
    //删除时间戳
    await Future.wait(events.map((e) => _eventStateDaoDBHelper
        .deleteByEventAutoId(e.autoId ?? 0, e.frame_id ?? "")));
  }

  _onDataSetChanged() async {
    var dbEvents = await _dbHelper.queryAll();
    var fold = dbEvents.fold<Map<String, List<EventDTO>>>({}, (map, event) {
      if (!map.containsKey(event.frame_id)) {
        map[event.frame_id ?? ""] = [];
      }
      map[event.frame_id ?? ""]!.add(event);
      return map;
    });
    _allEvent = fold;
    for (var frameId in _allEvent.keys) {
      ALogger.e(
          "_onDataSetChanged  : ${frameId} ${_allEvent[frameId]?.map((e) => {
                "${e.title} -${e.attendees}"
              })}");
    }
    notifyListeners();
  }
/**
 * {
    "frame_id": "111",
    "event_id": 1,
    "accountType": "gmail.com",
    "accountName": "Bidden@gmail.com",
    "calendarDisplayName": "Bidden_cal",
    "title": "竞选1",
    "allDay": 0,
    "dtstart": 1720452600000,
    "dtend": 1720456200000,
    "rrule": "FREQ=WEEKLY;WKST=SU;BYDAY=MO,TU,WE,TH,FR",
    "reminder": "5,10,15,2880",
    "eventTimezone": "Asia/Shanghai",
    "eventLocation": "洛杉矶",
    "description": "稳住了",
    "attendees": ["zhangsan@gmail.com", "lisi@hotmail.com", "wangwu@126.com"],
    "etag": 1720452600000,
    “finish”: 1
    }
 */
}

typedef EventDTOWrapper = (String, List<EventDTO>);

class EventsDBHelper {
  final String tableEvents = 'events';
  late Database _db;

  init() async {
    _db =
        await openDatabase("$tableEvents.db", version: 1, onCreate: _onCreate);
  }

  Future<void> _onCreate(Database db, int version) async {
    await db.execute('''
      CREATE TABLE $tableEvents (
        autoId INTEGER PRIMARY KEY AUTOINCREMENT, 
        id integer UNIQUE,
        frame_id text,
        event_id text not null,
        accountType text,
        accountName text,
        calendarDisplayName text,
        title text,
        allDay integer,
        dtstart integer,
        dtend integer,
        rrule text,
        reminder text,
        eventTimezone text,
        eventLocation text,
        description text,
        attendees text,
        etag integer,
        finish integer,
        sync integer not null DEFAULT 0,
        action integer not null DEFAULT 0 
        )
    ''');
  }

  Future<List<EventDTO>> queryAll() async {
    return await _db.query(tableEvents).then((e) {
      return e.map((e) => EventDTO.fromJson(e)).toList();
    });
  }

  Future<List<EventDTO>> queryNoSync() async {
    return await _db
        .query(tableEvents,
        columns: ["id", "sync"],
        where: 'id IS NULL OR sync = ?',
        whereArgs: [0])
        .then((e) {
      return e.map((e) => EventDTO.fromJson(e)).toList();
    });
  }

  Future<int> insert(EventDTO event) async {
    ALogger.e("insert enter: $event");
    return await _db.insert(
        tableEvents, filterNull(event.toMap()
      ..remove('autoId')),
        conflictAlgorithm: ConflictAlgorithm.replace);
  }

  Future<EventDTO?> getById(int id) async {
    List<Map<String, dynamic>> maps =
        await _db.query(tableEvents, where: 'id = ?', whereArgs: [id]);
    if (maps.isNotEmpty) {
      return EventDTO.fromJson(maps.first);
    }
    return null;
  }

  Future<int> delete(num id) async {
    return await _db.delete(tableEvents, where: 'id = ?', whereArgs: [id]);
  }

  Future<int> deleteAll() async {
    return await _db.delete(tableEvents);
  }

  Future<int> update(EventDTO event) async {
    return _db.update(
      tableEvents,
      filterNull(event.toMap()),
      where: 'autoId = ?',
      whereArgs: [event.autoId],
    );
  }
}

Map<String, dynamic> filterNull(Map map) {
  Map<String, Object> putMap = {};
  for (var key in map.keys) {
    var value = map[key];
    if (value != null) {
      putMap[key] = value;
    }
  }
  return putMap;
}

class EventStateDao {
  final String tableEventsStates = 'events_status';
  late Database _db;

  init() async {
    _db = await openDatabase("$tableEventsStates.db",
        version: 1, onCreate: _onCreate);
  }

  Future<void> _onCreate(Database db, int version) async {
    await db.execute('''
          CREATE TABLE $tableEventsStates (
            autoId INTEGER PRIMARY KEY AUTOINCREMENT,
            frame_id Text,
            id INTEGER,
            time INTEGER,
            eventAutoId INTEGER,
            UNIQUE(id, time, frame_id)
          )
        ''');
    await db.execute('''
        CREATE INDEX idx_id ON ${tableEventsStates} (id)
      ''');
    await db.execute('''
        CREATE UNIQUE INDEX idx_eventAutoId ON ${tableEventsStates} (eventAutoId)
      ''');
  }

  Future<int> insert(ScheduleEventStateEntity entity) async {
    ALogger.e("insert : ${entity.eventAutoId}");
    return await _db
        .insert(tableEventsStates, filterNull(entity.toMap()..remove('autoId')),
            conflictAlgorithm: ConflictAlgorithm.replace)
        .catchError((e) {
      ALogger.e(e);
    });
  }

  Future<ScheduleEventStateEntity?> selectByAutoId(int autoId) async {
    final List<Map<String, dynamic>> maps = await _db.query(
      tableEventsStates,
      where: 'autoId = ?',
      whereArgs: [autoId],
      limit: 1,
    );

    if (maps.isNotEmpty) {
      return ScheduleEventStateEntity.fromMap(maps.first);
    }
    return null;
  }

  Future<List<ScheduleEventStateEntity>?> selectByEventAutoId(
      int? eventAutoId) async {
    final List<Map<String, dynamic>> maps = await _db.query(
      tableEventsStates,
      where: 'eventAutoId = ?',
      whereArgs: [eventAutoId],
    );

    return List.generate(maps.length, (i) {
      return ScheduleEventStateEntity.fromMap(maps[i]);
    });
  }

  Future<List<ScheduleEventStateEntity>> getAll() async {
    final List<Map<String, dynamic>> maps = await _db.query(tableEventsStates);

    return List.generate(maps.length, (i) {
      return ScheduleEventStateEntity.fromMap(maps[i]);
    });
  }

  Future<int> update(ScheduleEventStateEntity entity) async {
    return await _db.update(
      tableEventsStates,
      entity.toMap(),
      where: 'autoId = ?',
      whereArgs: [entity.autoId],
    );
  }

  Future<int> delete(ScheduleEventStateEntity entity) async {
    return await _db.delete(
      tableEventsStates,
      where: 'autoId = ?',
      whereArgs: [entity.autoId],
    );
  }

  Future<int> deleteByExcludedIds(List<int> ids, String frameId) async {
    final String idList = ids.join(', ');
    return await _db.delete(
      tableEventsStates,
      where: 'id NOT IN ($idList) And frame_id = ? AND id IS NOT NULL',
      whereArgs: [frameId],
    );
  }

  Future<List<ScheduleEventStateEntity>> queryByIdAndTime(
      int eventAutoId, int time) async {
    final List<Map<String, dynamic>> maps = await _db.query(
      tableEventsStates,
      where: 'eventAutoId = ? AND time < ?',
      whereArgs: [eventAutoId, time],
    );

    return List.generate(maps.length, (i) {
      return ScheduleEventStateEntity.fromMap(maps[i]);
    });
  }

  Future<int> deleteByIdAndTime(int eventAutoId, int time) async {
    return await _db.delete(
      tableEventsStates,
      where: 'eventAutoId = ? AND time < ?',
      whereArgs: [eventAutoId, time],
    );
  }

  Future<int> deleteByEventId(int id, String frameId) async {
    return await _db.delete(
      tableEventsStates,
      where: 'id = ? And frame_id = ?',
      whereArgs: [id, frameId],
    );
  }

  Future<int> deleteByEventAutoId(int autoID, String frameId) async {
    return await _db.delete(
      tableEventsStates,
      where: 'autoID = ?',
      whereArgs: [autoID],
    );
  }
}

bool isLastDayOfMonth(DateTime date) {
  // 获取下个月的第一天
  DateTime firstDayOfNextMonth = DateTime(date.year, date.month + 1, 1);

  // 获取本月的最后一天
  DateTime lastDayOfCurrentMonth =
      firstDayOfNextMonth.subtract(Duration(days: 1));

  // 判断当前日期是否是本月的最后一天
  return date.year == lastDayOfCurrentMonth.year &&
      date.month == lastDayOfCurrentMonth.month &&
      date.day == lastDayOfCurrentMonth.day;
}

bool isLastDayOfYear(DateTime date) {
  return date.month == 12 && date.day == 31;
}

bool areSameDayInDifferentYears(DateTime date1, DateTime date2) {
  return date1.month == date2.month && date1.day == date2.day;
}

DateTime normalizeToMidnight(DateTime dateTime) {
  return dateTime.toLocal().copyWith(hour: 0, minute: 0, second: 0, millisecond: 0, microsecond: 0).toUtc();
}

DateTime normalizeToEndOfDay(DateTime dateTime) {
  return DateTime(dateTime.year, dateTime.month, dateTime.day, 23, 59, 59).toUtc();
}
